<h2>Why is this an issue?</h2>
<p>In C#, the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#is-operator"><code>is</code>
type testing operator</a> can be used to check if the run-time type of an object is compatible with a given type. If the object is not null, then the
<code>is</code> operator performs a cast, and so performing another cast following the check result is redundant.</p>
<p>This can impact:</p>
<ul>
  <li> Performance: Performing the type check and cast separately can lead to minor performance issues. While this might not be noticeable in small
  applications, it can add up in larger, more complex systems. </li>
  <li> Readability: The code is less readable and less clean because it requires two lines (and two operations) to achieve something that could be
  done in one. </li>
</ul>
<h2>How to fix it</h2>
<p>Use <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching">pattern macthing</a> to perform the check
and retrieve the cast result.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<pre data-diff-id="1" data-diff-type="noncompliant">
if (x is Fruit)  // Noncompliant
{
  var f = (Fruit)x; // or x as Fruit
  // ...
}
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
if (x is Fruit fruit)
{
  // ...
}
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
  <li> Microsoft Learn - <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast">Type-testing
  operators and cast expressions - <code>is</code>, <code>as</code>, <code>typeof</code> and casts</a> </li>
  <li> Microsoft Learn - <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is">is operator (C# reference)</a>
  </li>
  <li> Microsoft Learn - <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching">Pattern matching
  overview</a> </li>
</ul>
<h3>Benchmarks</h3>
<table>
  <colgroup>
    <col style="width: 25%;">
    <col style="width: 25%;">
    <col style="width: 25%;">
    <col style="width: 25%;">
  </colgroup>
  <thead>
    <tr>
      <th>Method</th>
      <th>Runtime</th>
      <th>Mean</th>
      <th>Standard Deviation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><p>IsPattern_Class</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>176.48 ns</p></td>
      <td><p>0.765 ns</p></td>
    </tr>
    <tr>
      <td><p>IsWithCast_Class</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>246.12 ns</p></td>
      <td><p>22.391 ns</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>IsPattern_Class</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>325.11 ns</p></td>
      <td><p>14.435 ns</p></td>
    </tr>
    <tr>
      <td><p>IsWithCast_Class</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>311.22 ns</p></td>
      <td><p>11.145 ns</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>IsPattern_Interface</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>26.77 ns</p></td>
      <td><p>1.123 ns</p></td>
    </tr>
    <tr>
      <td><p>IsWithCast_Interface</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>26.45 ns</p></td>
      <td><p>2.115 ns</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>IsPattern_Interface</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>119.80 ns</p></td>
      <td><p>5.411 ns</p></td>
    </tr>
    <tr>
      <td><p>IsWithCast_Interface</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>119.33 ns</p></td>
      <td><p>4.380 ns</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>IsPattern_ValueType</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>22.58 ns</p></td>
      <td><p>1.161 ns</p></td>
    </tr>
    <tr>
      <td><p>IsWithCast_ValueType</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>19.41 ns</p></td>
      <td><p>2.675 ns</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>IsPattern_ValueType</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>39.66 ns</p></td>
      <td><p>0.645 ns</p></td>
    </tr>
    <tr>
      <td><p>IsWithCast_ValueType</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>41.34 ns</p></td>
      <td><p>0.462 ns</p></td>
    </tr>
  </tbody>
</table>
<h4>Glossary</h4>
<ul>
  <li> <a href="https://en.wikipedia.org/wiki/Arithmetic_mean">Mean</a> </li>
  <li> <a href="https://en.wikipedia.org/wiki/Standard_deviation">Standard Deviation</a> </li>
</ul>
<p>The results were generated by running the following snippet with <a href="https://github.com/dotnet/BenchmarkDotNet">BenchmarkDotNet</a>:</p>
<pre>
private Random random = new Random(1);

private object ReturnSometimes&lt;T&gt;() where T : new() =&gt;
    random.Next(2) switch
    {
        0 =&gt; new T(),
        1 =&gt; new object(),
    };

[BenchmarkCategory("ValueType"), Benchmark(Baseline = true)]
public int IsPattern_ValueType()
{
    var i = ReturnSometimes&lt;int&gt;();
    return i is int d
        ? d
        : default;
}

[BenchmarkCategory("ValueType"), Benchmark]
public int IsWithCast_ValueType()
{
    var i = ReturnSometimes&lt;int&gt;();
    return i is int
        ? (int)i
        : default;
}

[BenchmarkCategory("Class"), Benchmark(Baseline = true)]
public DuplicateCasts IsPattern_Class()
{
    var i = ReturnSometimes&lt;DuplicateCasts&gt;();
    return i is DuplicateCasts d
        ? d
        : default;
}

[BenchmarkCategory("Class"), Benchmark]
public DuplicateCasts IsWithCast_Class()
{
    var i = ReturnSometimes&lt;DuplicateCasts&gt;();
    return i is DuplicateCasts
        ? (DuplicateCasts)i
        : default;
}

[BenchmarkCategory("Interface"), Benchmark(Baseline = true)]
public IReadOnlyList&lt;int&gt; IsPattern_Interface()
{
    var i = ReturnSometimes&lt;List&lt;int&gt;&gt;();
    return i is IReadOnlyList&lt;int&gt; d
        ? d
        : default;
}

[BenchmarkCategory("Interface"), Benchmark]
public IReadOnlyList&lt;int&gt; IsWithCast_Interface()
{
    var i = ReturnSometimes&lt;List&lt;int&gt;&gt;();
    return i is IReadOnlyList&lt;int&gt;
        ? (IReadOnlyList&lt;int&gt;)i
        : default;
}
</pre>
<p>Hardware configuration:</p>
<pre>
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update)
Intel Core Ultra 7 165H, 1 CPU, 22 logical and 16 physical cores
  [Host]               : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
  .NET 9.0             : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
  .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
</pre>

